[reward, trainer] feat: add managed multi-reward deployments - #524
Conversation
|
@chenyingshu PTAL. I've been working on this recently, but it still needs some time to be further refined. |
0784651 to
5b5b8a5
Compare
b0407c9 to
63e0760
Compare
f9b3bcd to
0c744b3
Compare
17ae9d9 to
5b83d29
Compare
5b83d29 to
96ec737
Compare
Signed-off-by: Trigger <Meng.Bo.Wang@outlook.com>
|
Verdict: the engine path is upstream-aligned, the async API shape is right, and the CPU test suite is genuinely good. Holding merge on three design items (RFC sync for the native backend, manager coupling, native placement addressing) — all are cheap to fix now and expensive to fix after configs pile up on top. The async ask from the earlier thread landed; one concurrency gap below. Blocking — design1. Native backend: right call, wrong paper trail — amend RFC #432 and pin the contract now
Native is the right call for this repo: PickScore, HPSv3, CLIP scorers and most small non-LLM reward checkpoints will never be a vLLM/SGLang serving priority. But RFC #432 — the governing design doc this PR implements — says the opposite in three places:
The boundary question needs answering deliberately, because the follow-up plan (CPU-native, FSDP) walks further in. Upstream verl's history is the cautionary tale: it deprecated and deleted its framework-hosted FSDP/Megatron reward-model workers ( Ask: update RFC #432 in this PR to make native a first-class backend, with the contract written down: (a) replica-only — no framework-side TP/PP/FSDP, models too big for one device belong in the engine backend; (b) the user-supplied executor class owns inference; (c) 2. Named models are locked to the visual manager, via a silent config rewrite
with open_dict(config.reward.reward_manager):
config.reward.reward_manager.name = "MultiVisualRewardManager"Two problems: The rewrite breaks non-default configs. Upstream resolves managers via The lock-in blocks half this repo's use cases. The fix is cheap because the abstraction already exists: Ask: (a) don't rewrite — validate: error if the configured manager is user-set or not multi-capable; (b) introduce a modality-neutral 3.
|
Signed-off-by: Trigger <Meng.Bo.Wang@outlook.com>
Signed-off-by: Trigger <Meng.Bo.Wang@outlook.com>
Signed-off-by: Trigger <Meng.Bo.Wang@outlook.com>
|
Thanks for the detailed follow-up review. I addressed the items that belong in this PR and recorded the larger migrations separately in #432.
I also fixed a regression found by the manual NPU run: synchronous scoring could call rollout sleep twice. The compatibility wrapper now delegates lifecycle ownership only to the async path. Validation for the current head:
Deferred follow-up work is tracked in the table on #432: unified |
Signed-off-by: Trigger <129651635+Sky-Trigger@users.noreply.github.com>
Signed-off-by: Trigger <129651635+Sky-Trigger@users.noreply.github.com>
Signed-off-by: Trigger <Meng.Bo.Wang@outlook.com>
zhtmike
left a comment
There was a problem hiding this comment.
Looks good for me, wait CI pass
|
@zhtmike CI passed |
What does this PR do?
Related to #432.
This PR adds opt-in named reward models under
reward.models. A training jobcan use multiple model-backed rewards, give each model an explicit lifecycle
and resource allocation, and keep model inference separate from reward
calculation.
Two backends are supported:
enginewraps the pinned upstreamverl.RewardModelManager. The currentlysupported and validated named-engine path is vLLM.
nativeloads a user-supplied model class inside accelerator-bound Rayreward workers for checkpoints that are not supported by the serving engine.
Each configured
reward.reward_functions.<term>.path/namefunction owns inputinterpretation and final score semantics. Engine models expose router/model
arguments; native models expose a
reward_modelinference handle.Native backend contract
The native backend is deliberately small:
placement.devicesentry creates onecomplete model replica;
executor.modelclass owns model construction andinference;
infer(*args, **kwargs)is an intentionally untyped internal contractbetween the native model adapter and its configured reward function;
infer()and optionalclose()methods aresupported.
Native TP/PP/FSDP is not implemented. A future sharded-native design needs an
RFC-level replica-group contract; a flat device list cannot distinguish
complete replicas from ranks within one sharded replica.
Resource ownership and placement
The trainer still selects one parent resource pool:
reward.reward_model.enable_resource_pool=falseselectsglobal_pool;reward.reward_model.enable_resource_pool=trueselects the dedicatedreward_pool.Engine allocations consume a contiguous prefix of that parent pool in model
configuration order, with one upstream
SubRayResourcePoolper engine model.Each native model receives an independent indexed allocation. Its
placement.devicesvalues are global logical bundle indices within theselected parent pool. They are not physical CUDA/NPU IDs and are not
tensor-parallel ranks. Indices may be non-contiguous, but must be in range and
cannot overlap another native model or the engine prefix.
For example:
When the global pool is selected, bundles unused by named rewards may still be
used by actor/rollout workers.
Reward manager behavior
Named-model aggregation currently supports the visual sample contract only.
Jobs must explicitly select:
The framework validates this requirement and does not silently rewrite a
user-provided reward manager. A modality-neutral manager and Audio/Visual
manager consolidation are follow-up work.
Lifecycle and async API
Engine and native models share one user-facing
offloadoption:offload: truewakes before scoring and sleeps afterward;offload: falsekeeps the model resident.Scoring is bracketed by wake/score/sleep with cleanup in
finally. Independentmodel groups execute concurrently inside one scoring call. A manager-level
single-flight lock serializes overlapping calls to
async_compute_rm_score(), preventing one request from sleeping models whileanother request is entering Ray dispatch.
compute_rm_score()remains the synchronous compatibility wrapper used bycurrent trainers.
Compatibility
reward.modelskeep the legacy single-model path.reward.reward_model.enable=truecannot be combined with named models.available when named models are absent.
The extension and migration guide is
docs/algo/named_reward_models.md.Validation
Validated in the
verl-omni:gpucontainer:python -m pytest -q \ tests/reward_loop/test_audio_reward_manager_on_cpu.py \ tests/reward_loop/test_multi_reward_manager_on_cpu.py \ tests/reward_loop/test_named_reward_models_on_cpu.py \ tests/reward_loop/test_reward_loop_manager_on_cpu.py \ tests/trainer/diffusion/test_v1_colocate_reward_on_cpu.py # 116 passedAlso passed:
py_compilefor changed Python modules;verl_omni;The NPU engine/native E2E recipe is
tests/special_e2e/run_qwen_image_edit_lora_v1_npu_engine_native.sh.It was manually rerun successfully on a 16-NPU setup after fixing duplicate
rollout sleep in the synchronous compatibility path. This manual result is
reported separately from CI.
Current limitations and follow-up work
MultiVisualRewardManager.